package keqiang.com.kq_flutter_core_widget.channel;

import android.app.Activity;
import android.content.Context;

import com.amap.api.location.AMapLocationClient;
import com.amap.api.location.AMapLocationClientOption;
import com.amap.api.location.AMapLocationQualityReport;
import com.hjq.permissions.OnPermissionCallback;
import com.hjq.permissions.Permission;
import com.hjq.permissions.XXPermissions;
import com.keqiang.base.DateUtil;
import com.keqiang.base.Logger;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import androidx.activity.ComponentActivity;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.Lifecycle.Event;
import androidx.lifecycle.Lifecycle.State;
import androidx.lifecycle.LifecycleEventObserver;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodCodec;
import io.flutter.plugin.common.PluginRegistry.ActivityResultListener;
import keqiang.com.kq_flutter_core_widget.BuildConfig;

/**
 * 原生定位功能
 *
 * @author Created by wanggaowan on 2023/4/10 09:28
 */
public class NativeLocationChannel extends MethodChannel {
    
    // 通道id
    private static final String CHANNEL = "flutter.keqiang.com/location";
    
    // 开启定位
    private static final String METHOD_START_LOCATION = "startLocation";
    // 停止定位
    private static final String METHOD_STOP_LOCATION = "stopLocation";
    // 关闭定位，释放资源
    private static final String METHOD_CLOSE_LOCATION = "closeLocation";
    // 是否永久拒绝定位权限
    private static final String METHOD_IS_PERMANENT_DENIED_LOCATION_PERMISSION = "isPermanentDeniedLocationPermission";
    // 打开设置定位权限界面
    private static final String METHOD_OPEN_SET_LOCATION_PERMISSION = "openSetLocationPermission";
    
    private static final int REQUEST_CODE_OPEN_SET_VIEW = 0x151;
    private ActivityResultListener mActivityResultListener;
    private Result mResult;
    private final HashSet<Result> mResultSet = new HashSet<>();
    private ActivityPluginBinding mBinding;
    private AMapLocationClient locationClient = null;
    // 是否开启了定位监听
    private boolean mStartListen;
    
    public NativeLocationChannel(@NonNull BinaryMessenger messenger) {
        super(messenger, CHANNEL);
        init();
    }
    
    public NativeLocationChannel(@NonNull BinaryMessenger messenger, @NonNull String name) {
        super(messenger, name);
        init();
    }
    
    public NativeLocationChannel(@NonNull BinaryMessenger messenger, @NonNull String name, @NonNull MethodCodec codec) {
        super(messenger, name, codec);
        init();
    }
    
    public NativeLocationChannel(@NonNull BinaryMessenger messenger, @NonNull String name, @NonNull MethodCodec codec, @Nullable BinaryMessenger.TaskQueue taskQueue) {
        super(messenger, name, codec, taskQueue);
        init();
    }
    
    public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
        Activity activity = binding.getActivity();
        mBinding = binding;
        if (activity instanceof ComponentActivity) {
            Lifecycle lifecycle = ((ComponentActivity) activity).getLifecycle();
            if (!lifecycle.getCurrentState().isAtLeast(State.INITIALIZED)) {
                return;
            }
            
            lifecycle.addObserver((LifecycleEventObserver) (source, event) -> {
                if (event == Event.ON_DESTROY) {
                    destroyLocation();
                    
                    if (mActivityResultListener != null) {
                        mBinding.removeActivityResultListener(mActivityResultListener);
                        mActivityResultListener = null;
                    }
                    mBinding = null;
                }
            });
        }
        
        if (mActivityResultListener != null) {
            binding.addActivityResultListener(mActivityResultListener);
            return;
        }
        
        mActivityResultListener = (requestCode, resultCode, data) -> {
            if (requestCode == REQUEST_CODE_OPEN_SET_VIEW) {
                isHaveLocationPermission();
                return true;
            }
            return false;
        };
        
        binding.addActivityResultListener(mActivityResultListener);
    }
    
    private void init() {
        setMethodCallHandler((call, result) -> {
            switch (call.method) {
                case METHOD_START_LOCATION:
                    mResultSet.add(result);
                    startLocation();
                    break;
                case METHOD_STOP_LOCATION:
                    stopLocation();
                    result.success(true);
                    break;
                case METHOD_CLOSE_LOCATION:
                    destroyLocation();
                    result.success(true);
                    break;
                case METHOD_IS_PERMANENT_DENIED_LOCATION_PERMISSION:
                    mResult = result;
                    isPermanentDeniedLocationPermission();
                    break;
                case METHOD_OPEN_SET_LOCATION_PERMISSION:
                    mResult = result;
                    openSetLocationPermission();
                    break;
                default:
                    result.notImplemented();
                    break;
            }
        });
    }
    
    // 初始化定位SDK
    private void initGaoDeSDK(Context context) {
        AMapLocationClient.updatePrivacyShow(context, true, true);
        AMapLocationClient.updatePrivacyAgree(context, true);
        // 初始化client
        try {
            locationClient = new AMapLocationClient(context);
        } catch (Exception e) {
            Logger.printStackTrace(e);
            return;
        }
        // 设置定位参数
        locationClient.setLocationOption(getDefaultOption());
        // 设置定位监听
        locationClient.setLocationListener(location -> {
            if (location == null) {
                setAllError("location failed");
            } else {
                Map<String, Object> resultMap = new HashMap<>();
                resultMap.put("address", location.getAddress());
                resultMap.put("longitude", location.getLongitude());
                resultMap.put("latitude", location.getLatitude());
                setAllResult(resultMap);
            }
            
            if (BuildConfig.DEBUG) {
                if (location != null) {
                    StringBuilder sb = new StringBuilder();
                    // errCode等于0代表定位成功，其他的为定位失败，具体的可以参照官网定位错误码说明
                    if (location.getErrorCode() == 0) {
                        sb.append("定位成功" + "\n");
                        sb.append("定位类型: ").append(location.getLocationType()).append("\n");
                        sb.append("经    度    : ").append(location.getLongitude()).append("\n");
                        sb.append("纬    度    : ").append(location.getLatitude()).append("\n");
                        sb.append("精    度    : ").append(location.getAccuracy()).append("米").append("\n");
                        sb.append("提供者    : ").append(location.getProvider()).append("\n");
                        
                        sb.append("速    度    : ").append(location.getSpeed()).append("米/秒").append("\n");
                        sb.append("角    度    : ").append(location.getBearing()).append("\n");
                        // 获取当前提供定位服务的卫星个数
                        sb.append("星    数    : ").append(location.getSatellites()).append("\n");
                        sb.append("国    家    : ").append(location.getCountry()).append("\n");
                        sb.append("省            : ").append(location.getProvince()).append("\n");
                        sb.append("市            : ").append(location.getCity()).append("\n");
                        sb.append("城市编码 : ").append(location.getCityCode()).append("\n");
                        sb.append("区            : ").append(location.getDistrict()).append("\n");
                        sb.append("区域 码   : ").append(location.getAdCode()).append("\n");
                        sb.append("地    址    : ").append(location.getAddress()).append("\n");
                        sb.append("兴趣点    : ").append(location.getPoiName()).append("\n");
                        // 定位完成的时间
                        sb.append("定位时间: ").append(DateUtil.getDateString(new Date(location.getTime()))).append("\n");
                    } else {
                        // 定位失败
                        sb.append("定位失败" + "\n");
                        sb.append("错误码:").append(location.getErrorCode()).append("\n");
                        sb.append("错误信息:").append(location.getErrorInfo()).append("\n");
                        sb.append("错误描述:").append(location.getLocationDetail()).append("\n");
                    }
                    
                    sb.append("***定位质量报告***").append("\n");
                    sb.append("* WIFI开关：").append(location.getLocationQualityReport().isWifiAble() ? "开启" : "关闭").append("\n");
                    sb.append("* GPS状态：").append(getGPSStatusString(location.getLocationQualityReport().getGPSStatus())).append("\n");
                    sb.append("* GPS星数：").append(location.getLocationQualityReport().getGPSSatellites()).append("\n");
                    sb.append("* 网络类型：").append(location.getLocationQualityReport().getNetworkType()).append("\n");
                    sb.append("* 网络耗时：").append(location.getLocationQualityReport().getNetUseTime()).append("\n");
                    sb.append("****************").append("\n");
                    // 定位之后的回调时间
                    sb.append("定位时间: ").append(DateUtil.getDateString(new Date(location.getTime()))).append("\n");
                    // 解析定位结果，
                    String result = sb.toString();
                    Logger.e(NativeLocationChannel.class.getSimpleName(), "result=" + result);
                } else {
                    Logger.e(NativeLocationChannel.class.getSimpleName(), "定位失败，loc is null");
                }
            }
        });
    }
    
    /**
     * 获取GPS状态的字符串
     *
     * @param statusCode GPS状态码
     */
    private String getGPSStatusString(int statusCode) {
        String str = "";
        switch (statusCode) {
            case AMapLocationQualityReport.GPS_STATUS_OK:
                str = "GPS状态正常";
                break;
            case AMapLocationQualityReport.GPS_STATUS_NOGPSPROVIDER:
                str = "手机中没有GPS Provider，无法进行GPS定位";
                break;
            case AMapLocationQualityReport.GPS_STATUS_OFF:
                str = "GPS关闭，建议开启GPS，提高定位质量";
                break;
            case AMapLocationQualityReport.GPS_STATUS_MODE_SAVING:
                str = "选择的定位模式中不包含GPS定位，建议选择包含GPS定位的模式，提高定位质量";
                break;
            case AMapLocationQualityReport.GPS_STATUS_NOGPSPERMISSION:
                str = "没有GPS定位权限，建议开启gps定位权限";
                break;
        }
        return str;
    }
    
    /**
     * 默认的定位参数
     *
     * @author hongming.wang
     * @since 2.8.0
     */
    private AMapLocationClientOption getDefaultOption() {
        AMapLocationClientOption mOption = new AMapLocationClientOption();
        // 可选，设置定位模式，可选的模式有高精度、仅设备、仅网络。默认为高精度模式
        mOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
        // 可选，设置是否gps优先，只在高精度模式下有效。默认关闭
        mOption.setGpsFirst(false);
        // 可选，设置网络请求超时时间。默认为30秒。在仅设备模式下无效
        mOption.setHttpTimeOut(30000);
        // 可选，设置定位间隔。默认为2秒
        mOption.setInterval(2000);
        // 可选，设置是否返回逆地理地址信息。默认是true
        mOption.setNeedAddress(true);
        // 可选，设置是否单次定位。默认是false
        mOption.setOnceLocation(false);
        // 可选，设置是否等待wifi刷新，默认为false.如果设置为true,会自动变为单次定位，持续定位时不要使用
        mOption.setOnceLocationLatest(false);
        // 可选， 设置网络请求的协议。可选HTTP或者HTTPS。默认为HTTP
        AMapLocationClientOption.setLocationProtocol(AMapLocationClientOption.AMapLocationProtocol.HTTP);
        // 可选，设置是否使用传感器。默认是false
        mOption.setSensorEnable(false);
        // 可选，设置是否开启wifi扫描。默认为true，如果设置为false会同时停止主动刷新，停止以后完全依赖于系统刷新，定位位置可能存在误差
        mOption.setWifiScan(true);
        // 可选，设置是否使用缓存定位，默认为true
        mOption.setLocationCacheEnable(true);
        // 可选，设置逆地理信息的语言，默认值为默认语言（根据所在地区选择语言）
        mOption.setGeoLanguage(AMapLocationClientOption.GeoLanguage.DEFAULT);
        return mOption;
    }
    
    // 设置回调的结果
    private void setResult(Object value) {
        if (mResult != null) {
            mResult.success(value);
            mResultSet.remove(mResult);
        }
        mResult = null;
    }
    
    private void setAllResult(Object value) {
        for (Result result : mResultSet) {
            result.success(value);
        }
        mResult = null;
        mResultSet.clear();
    }
    
    // 设置回调的错误结果
    private void setError(String msg) {
        if (mResult != null) {
            mResult.error("0", msg, null);
            mResultSet.remove(mResult);
        }
        mResult = null;
    }
    
    private void setAllError(String msg) {
        for (Result result : mResultSet) {
            result.error("0", msg, null);
        }
        mResult = null;
        mResultSet.clear();
    }
    
    // 开启定位
    private void startLocation() {
        Activity activity = mBinding == null ? null : mBinding.getActivity();
        if (activity == null) {
            setAllError("not attach to activity");
            return;
        }
    
        if(locationClient == null) {
            initGaoDeSDK(activity);
        }
        
        if (locationClient == null) {
            setAllError("location open failed");
            return;
        }
        
        XXPermissions.with(activity)
            .permission(Permission.ACCESS_FINE_LOCATION, Permission.ACCESS_COARSE_LOCATION)
            .request(new OnPermissionCallback() {
                @Override
                public void onGranted(List<String> permissions, boolean all) {
                    startLocation2();
                }
                
                @Override
                public void onDenied(List<String> permissions, boolean never) {
                    setAllError("no location permission");
                }
            });
    }
    
    // 启动定位
    private void startLocation2() {
        if (locationClient != null) {
            if (!mStartListen) {
                locationClient.startLocation();
                mStartListen = true;
            }
        } else {
            setAllError("location open failed");
        }
    }
    
    // 停止定位
    private void stopLocation() {
        if (locationClient != null && mStartListen) {
            locationClient.stopLocation();
        }
        mStartListen = false;
        setAllError("call stop location method");
    }
    
    // 释放定位资源
    private void destroyLocation() {
        if (locationClient != null) {
            locationClient.onDestroy();
            locationClient = null;
        }
        
        mResult = null;
        mResultSet.clear();
        mStartListen = false;
    }
    
    /**
     * 是否永久拒绝定位权限
     * （一定要在权限申请的回调方法中调用才有效果）
     */
    private void isPermanentDeniedLocationPermission() {
        Activity activity = mBinding == null ? null : mBinding.getActivity();
        if (activity == null || activity.isDestroyed()) {
            setError("not attach to activity");
            return;
        }
        
        boolean has = XXPermissions.isDoNotAskAgainPermissions(activity, Permission.ACCESS_COARSE_LOCATION, Permission.ACCESS_FINE_LOCATION);
        setResult(has);
    }
    
    /**
     * 是否拥有定位权限
     */
    private void isHaveLocationPermission() {
        Activity activity = mBinding == null ? null : mBinding.getActivity();
        if (activity == null || activity.isDestroyed()) {
            setResult(null);
            return;
        }
        
        boolean has = XXPermissions.isGranted(activity, Permission.ACCESS_COARSE_LOCATION, Permission.ACCESS_FINE_LOCATION);
        setResult(has);
    }
    
    // 打开设置定位权限界面
    private void openSetLocationPermission() {
        Activity activity = mBinding == null ? null : mBinding.getActivity();
        if (activity == null) {
            setError("not attach to activity");
            return;
        }
        
        List<String> permissions = new ArrayList<>();
        permissions.add(Permission.ACCESS_COARSE_LOCATION);
        permissions.add(Permission.ACCESS_FINE_LOCATION);
        XXPermissions.startPermissionActivity(activity, permissions, REQUEST_CODE_OPEN_SET_VIEW);
    }
}
